package cn.liberals.basic.service.impl;

import cn.liberals.basic.constant.BaseConstants;
import cn.liberals.basic.exception.BusinessException;
import cn.liberals.basic.service.IVerifyCodeService;
import cn.liberals.basic.utils.*;
import cn.liberals.basic.vo.RegisterInEmailVo;
import cn.liberals.basic.vo.RegisterVo;
import cn.liberals.user.domain.User;
import cn.liberals.user.mapper.UserMapper;
import org.apache.xmlbeans.impl.xb.ltgfmt.Code;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

@Service
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    /*
    * @Title: getImageCode
    * @Description: 图形验证码
    * @Author: liberals
    * @Version: 1.0
    * @Date:  2022/7/12 23:56
    * @Parameters:
    * @Return
    */
    @Override
    public String getImageCode(String key) {
        // 判断传递的key值是否为空，如果为空则抛出异常
        if(StringUtils.isEmpty(key)){
            throw new BusinessException("请传入参数");
        }
        // 调用工具类获取随机四位数
        String code = StrUtils.getComplexRandomString(4);

        // 将随机字符串设置到图形编码中，key就是传过来的值
        redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);

        // 将图形码转换成base64
        String base64Str = VerifyCodeUtils.VerifyCode(100, 35, code);

        // 返回
        return base64Str;
    }

    /*
    * @Title: smsCode
    * @Description: 发送短信验证码
    * @Author: liberals
    * @Version: 1.0
    * @Date:  2022/7/10 21:13
    * @Parameters:
    * @Return
    */
    @Override
    public void smsCode(RegisterVo registerVo) {
        // 获取参数
        String phone = registerVo.getPhone();

        // 业务类型type
        String type = registerVo.getType();
        //1.非空校验
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(type)){
            throw new BusinessException("参数不能为空！");
        }
        // 2.判断是走那个业务方法
        // 2.1.注册业务
        if ("register".equals(type)){
            // 校验图形验证码是否正确
            checkImageCode(registerVo);
            //3.判断用户是否已注册，如果已注册提示用户
            User user = userMapper.loadByPhone(phone);
            if (user != null){
                throw new BusinessException("用户已注册，请直接登录，如果忘记密码，请点击忘记密码！");
            }
            sendCode(BaseConstants.verifyCodeConstant.BUSINESS_REGISTER_KEY, phone);
        }else if ("binder".equals(type)){ // 2.2.微信绑定账号业务,发送短信验证码
            sendCode(BaseConstants.verifyCodeConstant.BUSINESS_BINDER_KEY, phone);
        }
    }

    /*
    * @Title: checkImageCode
    * @Description: 校验图形验证码
    * @Author: liberals
    * @Version: 1.0
    * @Date:  2022/7/10 21:13
    * @Parameters:
    * @Return
    */
    public void checkImageCode(RegisterVo registerVo){
        // 图形验证码的key
        String imageCodeKey = registerVo.getImageCodeKey();
        // 图形验证码的value
        String imageCodeValue = registerVo.getImageCodeValue();
        //1.非空校验
        if (StringUtils.isEmpty(imageCodeKey) || StringUtils.isEmpty(imageCodeValue)){
            throw new BusinessException("参数不能为空！");
        }
        //2.判断图形验证码是否正确以及存在
        Object imageCodeValueTmp = redisTemplate.opsForValue().get(imageCodeKey);
        if (imageCodeValueTmp == null){
            throw new BusinessException("验证码已过期，请重新获取！");
        }
        // 忽略大小写比较图形验证码是否正确
        if (!imageCodeValueTmp.toString().equalsIgnoreCase(imageCodeValue)){
            throw new BusinessException("验证码错误，请重新输入！");
        }
    }



    /*
    * @Title: sendCode
    * @Description: 发送手机验证码
    * @Author: liberals
    * @Version: 1.0
    * @Date:  2022/7/10 21:31
    * @Parameters:
    * @Return
    */
    public void sendCode(String businessKey, String phone){
        //4.根据电话号码拼接业务键，去redis中获取对应的value值，判断是否存在
        String phoneCodeKey = businessKey + phone;
        Object phoneCodeValue = redisTemplate.opsForValue().get(phoneCodeKey);
        //0.定义一个空字符串，专门用来接收验证码
        String phoneCode = "";
        if (phoneCodeValue == null){//1.不存在
            //1.生成新的随机验证码,并赋值给空字符串
            phoneCode = StrUtils.getComplexRandomString(4);
        }else {//2.存在
            //1.分割value得到时间戳，跟现在的时间戳做减法，如果小于1分钟的毫秒数抛出异常
            String[] phoneCodeValueTmp = phoneCodeValue.toString().split(":");
            // 之前存入code时，我们记录的时间戳
            Long oldTime = Long.valueOf(phoneCodeValueTmp[0]);
            // 现在的时间戳
            Long nowTime = System.currentTimeMillis();
            if (nowTime - oldTime < 1*60*1000){
                throw new BusinessException("请一分钟之后再获取！");
            }
            //2.将旧的验证码赋值给空字符串
            phoneCode = phoneCodeValueTmp[1];
        }
        //5.通过业务键+phone作为key，验证码+时间戳作为value，存入redis，设置为三分钟有效
        String phoneCodeRedis = System.currentTimeMillis() + ":" + phoneCode;
        redisTemplate.opsForValue().set(phoneCodeKey, phoneCodeRedis, 3, TimeUnit.MINUTES);
        //6.发送短信验证码
        // SmsUtils.sendCode(phone, "亲，您的验证码是："+ phoneCode);
        System.out.println("亲，您的验证码是："+ phoneCode);
    }


    @Override
    public void emailCode(RegisterInEmailVo registerInEmailVo) {
        String email = registerInEmailVo.getEmail();
        String imageCodeKey = registerInEmailVo.getImageCodeKey();
        String imageCodeValue = registerInEmailVo.getImageCodeValue();
        // 1.判断参数非空
        if(email==null){
            throw new BusinessException("电子邮箱为空，请输入参数");
        }
        if(imageCodeKey==null || imageCodeValue==null){
            throw new BusinessException("图形验证码参数为空，请输入参数");
        }

        // 2.判断Redis中图形验证码是否存在
        Object imageCodeValueTem = redisTemplate.opsForValue().get(imageCodeKey);
        // 2.1不存在，都当过期处理，抛出异常
        if (imageCodeValueTem==null){
            throw new BusinessException("图形验证码失效，请重新输入");
        }
        // 2.2存在，判断图形验证码跟输入的是否一致，不一致抛出异常
        if (!imageCodeValueTem.toString().equalsIgnoreCase(imageCodeValue)){
            throw new BusinessException("图形验证码错误，请重新输入");
        }
        // 3.判断邮箱是否已经注册，注册过抛出异常
        User user = userMapper.loadByEmail(email);
        if(user!=null){
            throw new BusinessException("此邮箱已被注册，请重新输入！");
        }
        // 4判断是否已经获取过验证码
        String emailVerifyCodeKey = BaseConstants.verifyCodeConstant.BUSINESS_REGISTER_KEY +":"+ email;
        Object emailVerifyCodeValueTem = redisTemplate.opsForValue().get(emailVerifyCodeKey);
        String emailCode = "";
        String emailVerifyCodeValue = "";
        // 4.2 不是第一次获取验证码
        if (emailVerifyCodeValueTem!=null){
            // 判读是否超过一分钟，如果小于。抛出异常
            String[] emailCodeTemp = emailVerifyCodeValueTem.toString().split(":");
            Long phoneCodeCurrentTime = Long.valueOf(emailCodeTemp[0]);
            if(System.currentTimeMillis()-phoneCodeCurrentTime< 1*60*1000){
                throw new BusinessException("操作过于频繁，请一分钟后重新获取！");
            }
        }
        // 4.2第一次获取验证码
        // 调用方法。生成随机4位验证码
        emailCode = StrUtils.getComplexRandomString(4);
        emailVerifyCodeValue = System.currentTimeMillis() +":"+ emailCode;
        // 5.设置到redis中,并设置三分钟有效
        redisTemplate.opsForValue().set(emailVerifyCodeKey,emailVerifyCodeValue,3,TimeUnit.MINUTES);
        // 6.发送邮箱验证码提醒
        MailUtils.sendMail(email,"您的邮箱验证码为:"+emailCode,"宠物之家邮箱注册验证码");
    }

    @Override
    public void mobileCode(RegisterVo registerVo) {
        String phone = registerVo.getPhone();
        // 1.判断合法性
        if (StringUtils.isEmpty(phone)){
            throw new BusinessException("电话号码为空，请输入参数！");
        }
        if (!ValidateUtil.validatePhone(phone)){
            throw new BusinessException("请输入有效的手机号！");
        }
        // 2.判断是否是第一次获取验证码
        // 2.1不是，如果大于1分钟直接从redis中查出验证码，发送，并重新设置有效时间,间隔小于一分钟，提示操作频繁
        //2.2是第一次，生成随机验证码，设置到redis，发送短信。
        String phoneVerifyCodeKey = BaseConstants.verifyCodeConstant.BUSINESS_LOGIN_KEY +":"+ phone;
        Object phoneVerifyCodeValueTem = redisTemplate.opsForValue().get(phoneVerifyCodeKey);
        String phoneCode = "";
        String phoneVerifyCodeValue = "";
        // 4.2 不是第一次获取验证码
        if (phoneVerifyCodeValueTem!=null){
            String[] phoneCodeTemp = phoneVerifyCodeValueTem.toString().split(":");
            Long phoneCodeCurrentTime = Long.valueOf(phoneCodeTemp[0]);
            phoneCode = phoneCodeTemp[1];
            // 判读是否超过一分钟，如果小于。抛出异常
            if(System.currentTimeMillis()-phoneCodeCurrentTime< 1*60*1000){
                throw new BusinessException("操作过于频繁，请一分钟后重新获取！");
            }else {
                // 超过一分钟，小于三分钟的情况
//                VerifyCodeUtils.SendSmsVerifyCode(phone,"您的验证码为：" + phoneCode);
                System.out.println(phoneCode);
                redisTemplate.opsForValue().set(phoneVerifyCodeKey,phoneVerifyCodeValue,3,TimeUnit.MINUTES);
                return;
            }
        }
        // 4.2第一次获取验证码
        // 调用方法。生成随机4位验证码
        phoneCode = StrUtils.getComplexRandomString(4);
        phoneVerifyCodeValue = System.currentTimeMillis() +":"+ phoneCode;
        // 5.设置到redis中,并设置三分钟有效
        redisTemplate.opsForValue().set(phoneVerifyCodeKey,phoneVerifyCodeValue,3,TimeUnit.MINUTES);
        // 6.发送短信提醒
        // VerifyCodeUtils.SendSmsVerifyCode(phone,"您的验证码为：" + phoneCode);
        System.out.println(phoneCode);
    }
}
